/* Copyright (c) 2015,2016 Lucky Byte, Inc.
 */
package com.lucky_byte.pay.jar;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.TemplateModelException;

/**
 * FreeMarker 模板引擎
 */
public class FreeMarker
{
	private static final Logger logger = LogManager.getLogger();
	private static FreeMarker instance = null;

	private Configuration config;

	/**
	 * 获取全局唯一对象
	 */
	public static FreeMarker getInstance() {
		try {
			if (instance == null) {
				instance = new FreeMarker();
			}
			return instance;
		} catch (TemplateModelException | IOException e) {
			logger.catching(e);
			return null;
		}
	}

	/**
	 * 创建 FreeMarker 处理引擎
	 */
	private FreeMarker() throws IOException, TemplateModelException {
		config = new Configuration(Configuration.VERSION_2_3_23);
		config.setLogTemplateExceptions(true);
		config.setTemplateExceptionHandler(
				TemplateExceptionHandler.RETHROW_HANDLER);
		config.setLocale(Locale.CHINESE);
		config.setDefaultEncoding("UTF-8");
		config.setOutputEncoding("UTF-8");
		config.setAPIBuiltinEnabled(true);

		Map<String, Object> jdbc = new HashMap<>();
		jdbc.put("listby", new FMJdbcListBy());
		config.setSharedVariable("jdbc", jdbc);
	}

	/** 
	 * 处理 FreeMarker 模板
	 *
	 * @param input_file 模板文件
	 * @param input_enc 模板文件编码
	 * @param output_file 输出文件
	 * @param output_enc 输出文件编码
	 * @param model 传递给模板的数据
	 * @return 输出文件，或 null
	 */
	public File generate(File input_file, String input_enc,
			File output_file, String output_enc, Map<String, Object> model) {
		try {
			if (!input_file.isAbsolute()) {
				File file = Runtime.fileFor(input_file.getPath());
				if (file == null) {
					logger.error("未找到 FreeMarker 模板文件[{}]，处理失败.",
							input_file.getPath());
					return null;
				}
				input_file = file;
			}
			if (!input_file.exists() || !input_file.isFile()) {
				logger.error("模板文件[{}]不存在或不是常规文件，处理失败.",
						input_file.getPath());
				return null;
			}
			File tmpl_load_path = input_file.getParentFile();
			if (tmpl_load_path == null) {
				logger.error("输入文件[{}]的路径无效.", input_file.getPath());
				return null;
			}
			config.setDirectoryForTemplateLoading(tmpl_load_path);

			if (output_file == null) {
				String[] array = input_file.getName().split("\\.");
				String suffix = "." + array[array.length - 1];
				output_file = File.createTempFile("ftl-", suffix);
			} else {
				File parent = output_file.getParentFile();
				if (parent != null && !parent.exists()) {
					if (!parent.mkdirs()) {
						logger.error("创建目录[{}]失败.", parent.getPath());
						return null;
					}
				}
			}
			FileOutputStream ostream = new FileOutputStream(output_file);
			BufferedWriter writer = new BufferedWriter(
				new OutputStreamWriter(ostream, output_enc));

			if (model == null) {
				model = new HashMap<>();
			}
			putSystemVars(model);

			Template tmpl = config.getTemplate(input_file.getName(), input_enc);
			tmpl.process(model, writer);
			writer.close();
			return output_file;
		} catch (Exception e) {
			logger.error("处理 FreeMarker 模板[{}]错误[{}].",
					input_file.getAbsolutePath(), e.getMessage());
			return null;
		}
	}

	/**
	 * 提供常用数据供模板使用
	 */
	private void putSystemVars(Map<String, Object> model) {
		model.put("APPNAME", Version.APPNAME);
		model.put("VERSION", Version.VERSION);
		model.put("VENDOR", Version.VENDOR);
		model.put("COPYRIGHT", Version.COPYRIGHT);

		Calendar calendar = Calendar.getInstance();
		model.put("YEAR", calendar.get(Calendar.YEAR));
		model.put("MONTH", calendar.get(Calendar.MONTH) + 1);
		model.put("DAY_OF_MONTH", calendar.get(Calendar.DAY_OF_MONTH));
		model.put("DAY_OF_YEAR", calendar.get(Calendar.DAY_OF_YEAR));
		model.put("HOUR", calendar.get(Calendar.HOUR));
		model.put("HOUR_OF_DAY", calendar.get(Calendar.HOUR_OF_DAY));
		model.put("MINUTE", calendar.get(Calendar.MINUTE));
		model.put("SECOND", calendar.get(Calendar.SECOND));
		model.put("TODAY", String.format("%d 年 %d 月 %d 日",
				calendar.get(Calendar.YEAR),
				calendar.get(Calendar.MONTH) + 1,
				calendar.get(Calendar.DAY_OF_MONTH)));
		model.put("TIME", String.format("%d:%d:%d",
				calendar.get(Calendar.HOUR_OF_DAY),
				calendar.get(Calendar.MINUTE),
				calendar.get(Calendar.SECOND)));
	}
}
